Skip to content

feat: mask ||spoiler|| text until the message is revealed#238

Open
RoJoPM wants to merge 1 commit into
chojs23:mainfrom
RoJoPM:feat/reveal-spoilers
Open

feat: mask ||spoiler|| text until the message is revealed#238
RoJoPM wants to merge 1 commit into
chojs23:mainfrom
RoJoPM:feat/reveal-spoilers

Conversation

@RoJoPM

@RoJoPM RoJoPM commented Jul 2, 2026

Copy link
Copy Markdown

Fixes #204.

Problem

Discord spoiler syntax ||text|| was rendered as plain text (pipes and all), so spoilers were never hidden.

Approach

Spoilers reuse the existing byte-range highlight pipeline (the same mechanism as URLs and mentions):

  • Add a Spoiler TextHighlightKind whose paint style sets fg == bg, so the masked text renders as a solid block. Because the mask is the same width as the hidden text, revealing never changes layout or invalidates the row-metrics cache.
  • spoiler_ranges() detects ||...|| spans (requires non-empty content; ignores |||| and unclosed openers).
  • Revealed messages are tracked in an ephemeral, per-session set on DashboardState, keyed by message id — spoilers re-hide on restart, matching Discord.

Interaction (mouse)

A single left-click on a message toggles its spoilers (reveal / re-hide). A double-click leaves them revealed and still activates the message. Only messages that actually contain a spoiler participate, so ordinary clicks never accumulate state.

Tests

  • spoiler_ranges unit tests (single / multiple / empty / unclosed / plain).
  • State-level tests for the click toggle and the no-spoiler no-op.
  • Full suite green (cargo test --lib: 1546 passed), cargo clippy clean.

Known limitations (happy to address based on your preference)

  1. A revealed spoiler shows the literal ||text|| (pipes visible), kept intentionally so hidden/revealed widths are identical and nothing reflows. Can strip the pipes on reveal if preferred.
  2. Scope is the main message body; spoilers inside embeds / replies / poll text aren't masked yet.

🤖 Generated with Claude Code

Discord spoiler syntax (||text||) was rendered as plain text including the
pipes (chojs23#204). Detect ||...|| spans and mask them as a solid block until the
reader reveals the message by clicking it.

- Add a Spoiler TextHighlightKind whose paint style sets fg == bg, reusing
  the existing byte-range highlight pipeline (same mechanism as URLs/mentions).
  Because the mask is the same width as the hidden text, revealing never
  changes layout and does not invalidate the row-metrics cache.
- Detect spans with spoiler_ranges() (non-empty content, ignores |||| and
  unclosed openers), with unit tests.
- Track revealed messages in an ephemeral per-session set on DashboardState,
  keyed by message id, so spoilers re-hide on restart like Discord.
- Single left-click on a message toggles its spoilers; a double-click leaves
  them revealed and still activates the message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: spoiler text and images not hidden

1 participant